/*  Copyright (c) 2011, Philip Busch <vzxwco@gmail.com>
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *   - Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <ctype.h>
#include <signal.h>
#include <errno.h>
#include "5.h"

static void print_usage(void);
static void print_help(void);
static void die(char *msg);
static void go_daemon(void);
static void cleanup(void);
static void sigint_handler(int num);
static int  set_default_config();
static int  handle_argv(int argc, char *argv[]);

int main(int argc, char *argv[])
{
	nc *core;
	char *addr, *port;

	atexit(&cleanup);
	signal(SIGINT, &sigint_handler);

	if (set_default_config() < 0)
		die(strerror(errno));

	if (handle_argv(argc, argv) < 0)
		exit(EXIT_FAILURE);

	core = nc_create(NC_SERVER);

	core_register_callbacks(core);

	addr = config_get("bind-address");
	port = config_get("bind-port");

	if (nc_dial(core, addr, port, NC_AF_AUTO, 1) < 0)
		die(nc_strerror(core));

	nc_talk();

	/* your kung-fu is old and now you must die!! */
	return EXIT_SUCCESS;
}

static void die(char *msg)
{
	fprintf(stderr, "%s: %s\n", PROG_NAME, msg);
	exit(EXIT_FAILURE);
}

static int set_default_config()
{
	if (config_set_with_desc("bind-address", PROG_PORT, \
		"bind to this host interface")) return -1;
	if (config_set_with_desc("bind-port", PROG_PORT, \
		"listen for incoming connections on this port")) return -1;
	if (config_set_with_desc("daemon-mode", "false", \
		"start 5 as a daemon")) return -1;

	return 0;
}

static int handle_argv(int argc, char *argv[])
{
	int c, line;
	extern char *optarg;
	extern int optind, opterr, optopt;
	char path[BUFSIZ];

	if (config_get_default_path(path, BUFSIZ, PROG_NAME, "5rc") < 0) {
		fprintf(stderr, "%s: config_get_default_path(): %s", PROG_NAME, config_strerror());
		return -1;
	}

	config_set("configfile", path);

	opterr = 0;
	while((c = getopt(argc, argv, ":vhda:p:c:s")) != -1) {
		switch(c) {
			case 'd':
				config_set("daemon-mode", "true");
				config_lock("daemon-mode");
				break;
			case 'a':
				config_set("bind-address", optarg);
				config_lock("bind-address");
				break;
			case 'p':
				config_set("bind-port", optarg);
				config_lock("bind-port");
				break;
			case 'c':
				config_set("configfile", optarg);
				config_lock("configfile");
				break;
			case 's':
				config_set("showconfig", "true");
				config_lock("showconfig");
				break;
			case 'h':
				print_help();
				return -1;
			case '?':
				if(isprint(optopt)) {
					fprintf(stderr, "%s: unknown option -%c\n", PROG_NAME, optopt);
				} else {
					fprintf(stderr, "%s: unknown option character \\x%x\n", PROG_NAME, optopt);
				}
				return -1;
			case ':':
				fprintf(stderr, "%s: option -%c requires an argument\n", PROG_NAME, optopt);
				return -1;
		}
	}

	line = config_read_file(config_get("configfile"));

	switch (line) {
		case 0:
			break;
		case -1:
			fprintf(stderr, "%s: %s\n", PROG_NAME, config_strerror());
			config_free();
			return -1;
		default:
			fprintf(stderr, "%s: %s: line %d: %s\n", PROG_NAME, config_get("configfile"), line, config_strerror());
			config_free();
			return -1;
	}

	while(argv[optind] != NULL) {
		if (config_read_mem(argv[optind], strlen(argv[optind])) < 0) {
			fprintf(stderr, "%s: %s\n", PROG_NAME, config_strerror());
			return -1;
		}
		optind++;
	}

	if (argv[optind] != NULL) {
		fprintf(stderr, "%s: \"%s\": sorry... what?\n", PROG_NAME, argv[optind]);
		return -1;
	}


	if (config_get("showconfig") != NULL) {
		config_print();
		return -1;
	}

	return 0;
}


static void print_usage(void)
{
	puts("USAGE: " PROG_NAME " [option(s)] -- [key=val]");
}

static void print_help(void)
{
	puts(PROG_NAME " v" PROG_VERSION " - high performance IRC engine\n");
	print_usage();
	puts("\nSupported options [defaults in brackets]:\n");
	puts("   -c <file>  use different config file");
	puts("   -a <addr>  bind to specific address");
	printf("   -p <port>  use different port [%s]\n", PROG_PORT);
	puts("   -d         daemon mode");
	puts("   -v         verbose output");
	puts("   -s         print config and exit");
	puts("   -h         print this help and exit");
	printf("\nSee %s for more information.\n\n", PROG_URL);
}

// some systems have a daemon() function, so we have to use this stupid name
static void go_daemon(void)
{
	if(fork() > 0)
		_exit(0);

	setsid();

	dup2(open("/dev/null", O_RDONLY), 0);
	dup2(open("/dev/null", O_WRONLY), 1);
	dup2(open("/dev/null", O_WRONLY), 2);

	chdir("/");
}

static void cleanup(void)
{
	config_free();
}

static void sigint_handler(int num)
{
	fprintf(stderr, "\rCaught signal SIGINT, cleaning up...\n");
	cleanup();

	signal(num, SIG_DFL);
	raise(num);
}



